package com.hrbxlh.virtualization.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.hrbxlh.virtualization.dao.ClusterMapper;
import com.hrbxlh.virtualization.dao.ImageAllocationMapper;
import com.hrbxlh.virtualization.dao.ImageAllocationMapperExt;
import com.hrbxlh.virtualization.dao.ImageMapper;
import com.hrbxlh.virtualization.dto.CourseDTO;
import com.hrbxlh.virtualization.dto.ImageAllocationDTO;
import com.hrbxlh.virtualization.enums.ImageTypeEnum;
import com.hrbxlh.virtualization.exception.GlobalException;
import com.hrbxlh.virtualization.param.CourseQueryParam;
import com.hrbxlh.virtualization.param.ImageAllocationCancelParam;
import com.hrbxlh.virtualization.param.ImageAllocationConfirmParam;
import com.hrbxlh.virtualization.pojo.Cluster;
import com.hrbxlh.virtualization.pojo.Image;
import com.hrbxlh.virtualization.pojo.ImageAllocation;
import com.hrbxlh.virtualization.pojo.ImageAllocationExample;
import com.hrbxlh.virtualization.result.ResultEnum;
import com.hrbxlh.virtualization.service.ClusterService;
import com.hrbxlh.virtualization.service.ImageAllocationService;
import com.hrbxlh.virtualization.vo.ChapterImageAllocationVO;
import com.hrbxlh.virtualization.vo.CourseImageAllocationVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author cheer
 */
@Service
public class ImageAllocationServiceImpl implements ImageAllocationService {

    @Autowired
    private ImageAllocationMapper imageAllocationMapper;

    @Autowired
    private ImageAllocationMapperExt imageAllocationMapperExt;

    @Autowired
    private ClusterMapper clusterMapper;

    @Autowired
    private ImageMapper imageMapper;

    @Autowired
    private ClusterService clusterService;

    @Override
    public boolean isClusterAllocated(Long clusterId) {
        return imageAllocationMapperExt.selectIdByClusterIdLimit1(clusterId) != null;
    }

    @Override
    public void deleteImageAllocationByClusterId(Long clusterId) {
        ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
        imageAllocationExample.or().andClusterEqualTo(true)
                .andClusterIdEqualTo(clusterId);
        imageAllocationMapper.deleteByExample(imageAllocationExample);
    }

    @Override
    @SuppressWarnings("unchecked")
    public PageInfo<CourseImageAllocationVO> listImageAllocationsByPage(
            Integer pageNum, Integer pageSize, CourseQueryParam courseQueryParam) {

        courseQueryParam.trimToNull();

        PageHelper.startPage(pageNum, pageSize);
        List<CourseDTO> courseDTOList = imageAllocationMapperExt.listCourseDTOs(courseQueryParam);
        PageInfo pageInfo = new PageInfo(courseDTOList);

        List<CourseImageAllocationVO> courseImageAllocationVOList = new ArrayList<>();
        for (CourseDTO courseDTO : courseDTOList) {
            CourseImageAllocationVO courseImageAllocationVO = courseDTO.convertToCourseImageAllocationVO();
            List<ImageAllocationDTO> imageAllocationDTOList =
                    imageAllocationMapperExt.listImageAllocationsByCourseId(courseDTO.getCourseId());
            fillImageAllocationToCourse(imageAllocationDTOList, courseImageAllocationVO);
            courseImageAllocationVOList.add(courseImageAllocationVO);
        }
        pageInfo.setList(courseImageAllocationVOList);
        return pageInfo;
    }

    @Override
    public List<ChapterImageAllocationVO> queryImageAllocation(Integer courseId) {
        List<ImageAllocationDTO> imageAllocationDTOList =
                imageAllocationMapperExt.listImageAllocationsByCourseId(courseId);

        List<ChapterImageAllocationVO> chapterImageAllocationVOList = new ArrayList<>();
        for (ImageAllocationDTO imageAllocationDTO : imageAllocationDTOList) {
            ChapterImageAllocationVO chapterImageAllocationVO = imageAllocationDTO.convertToChapterImageAllocationVO();
            chapterImageAllocationVOList.add(chapterImageAllocationVO);
        }
        return chapterImageAllocationVOList;
    }

    @Override
    public List<ImageAllocationDTO> queryImageAllocationByChapterId(Integer chapterId) {
        List<ImageAllocationDTO> imageAllocationDTOList = imageAllocationMapperExt.listImageAllocationsByChapterId(chapterId);

        return imageAllocationDTOList.stream().distinct().collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void confirmImageAllocation(ImageAllocationConfirmParam imageAllocationConfirmParam) {
        checkFlavorValid(imageAllocationConfirmParam.getCluster(), imageAllocationConfirmParam.getFlavor());
        if (imageAllocationConfirmParam.getCluster()) {
            checkClusterExists(imageAllocationConfirmParam.getClusterId());
        } else {
            checkImageExists(imageAllocationConfirmParam.getImageId());
        }

        for (Integer chapterId : imageAllocationConfirmParam.getChapterIds()) {
            ImageAllocation imageAllocation = imageAllocationConfirmParam.convertToImageAllocation(chapterId);
            imageAllocationMapperExt.insertOrUpdate(imageAllocation);
            if (isChapterReleased(chapterId)) {
                releaseImage(imageAllocation);
            }
        }
    }

    @Override
    public void cancelImageAllocation(ImageAllocationCancelParam imageAllocationCancelParam) {
        List<Integer> chapterIdList = imageAllocationCancelParam.getChapterIds();
        if(CollectionUtils.isEmpty(chapterIdList)){
            return;
        }

        ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
        imageAllocationExample.or().andChapterIdIn(chapterIdList);
        if(CollectionUtils.isEmpty(imageAllocationMapper.selectByExample(imageAllocationExample))){
            throw new GlobalException(ResultEnum.IMAGE_ALLOCATION_CANCEL_NOT_EXISTS);
        }

        imageAllocationExample.clear();
        imageAllocationExample.or().andCourseIdEqualTo(imageAllocationCancelParam.getCourseId())
                .andChapterIdIn(imageAllocationCancelParam.getChapterIds());
        imageAllocationMapper.deleteByExample(imageAllocationExample);
    }

    @Override
    public void release(Integer courseId) {
        List<Integer> chapterIdList = listChapterIdsByCourseId(courseId);

        if (CollectionUtils.isNotEmpty(chapterIdList)) {
            ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
            imageAllocationExample.or().andChapterIdIn(chapterIdList).andCourseIdEqualTo(courseId);
            List<ImageAllocation> imageAllocationList = imageAllocationMapper.selectByExample(imageAllocationExample);

            releaseImage(imageAllocationList);
        }
    }

    @Override
    public void confirmImageAllocation(Integer courseId, List<Integer> chapterIdList) {
        if(CollectionUtils.isNotEmpty(chapterIdList)){
            ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
            imageAllocationExample.or().andCourseIdEqualTo(0).andChapterIdIn(chapterIdList);
            ImageAllocation imageAllocation = new ImageAllocation();
            imageAllocation.setCourseId(courseId);
            imageAllocationMapper.updateByExampleSelective(imageAllocation, imageAllocationExample);
        }
    }

    @Override
    public void cancelImageAllocation(Integer courseId, List<Integer> chapterIdList) {
        if(CollectionUtils.isNotEmpty(chapterIdList)){
            ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
            imageAllocationExample.or().andCourseIdEqualTo(courseId).andChapterIdIn(chapterIdList);
            imageAllocationMapper.deleteByExample(imageAllocationExample);
        }
    }

    @Override
    public boolean canUnshare(Long imageId) {
        List<Cluster> clusterList = clusterService.listClustersByImageId(imageId);
        List<Long> clusterIdList = clusterList.stream().map(Cluster::getId)
                .collect(Collectors.toList());

        ImageAllocationExample imageAllocationExample = new ImageAllocationExample();
        imageAllocationExample.or().andImageIdEqualTo(imageId);
        if(CollectionUtils.isNotEmpty(clusterIdList)){
            imageAllocationExample.or().andClusterIdIn(clusterIdList);
        }
        List<ImageAllocation> imageAllocationList = imageAllocationMapper.selectByExample(imageAllocationExample);

        List<Integer> courseIdList = imageAllocationList.stream().map(ImageAllocation::getCourseId)
                .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(courseIdList) &&
                imageAllocationMapperExt.selectReleaseCourseIdLimit1(courseIdList)!=null){
            return false;
        }
        return true;
    }

    private List<Integer> listChapterIdsByCourseId(Integer courseId) {
        return imageAllocationMapperExt.listChapterIdsByCourseId(courseId);
    }

    private boolean isChapterReleased(Integer chapterId) {
        Integer result = imageAllocationMapperExt.selectReleaseCourseIdByChapterId(chapterId);
        return result != null;
    }

    private void releaseImage(ImageAllocation imageAllocation) {
        List<ImageAllocation> imageAllocationList = Lists.newArrayList(imageAllocation);
        releaseImage(imageAllocationList);
    }

    private void releaseImage(List<ImageAllocation> imageAllocationList) {
        for (ImageAllocation imageAllocation : imageAllocationList) {
            if (imageAllocation.getCluster()) {
                Cluster cluster = clusterMapper.selectByPrimaryKey(imageAllocation.getClusterId());
                cluster.setShared(true);
                clusterMapper.updateByPrimaryKeySelective(cluster);
                releaseCluster(cluster);
            } else {
                shareImage(imageAllocation.getImageId());
            }
        }
    }

    private void releaseCluster(Cluster cluster) {
        shareImage(cluster.getMasterImageId());
        shareImage(cluster.getFirstSlaveImageId());
        shareImage(cluster.getSecondSlaveImageId());
    }

    private void shareImage(Long imageId) {
        Image image = imageMapper.selectByPrimaryKey(imageId);
        if(image != null){
            image.setShared(true);
            imageMapper.updateByPrimaryKeySelective(image);
        }
    }

    private void fillImageAllocationToCourse(List<ImageAllocationDTO> imageAllocationDTOList,
            CourseImageAllocationVO courseImageAllocationVO) {

        int total = imageAllocationDTOList.size();
        int allocated = 0;

        Set<CourseImageAllocationVO.Image> imageSet = new HashSet<>();
        for (ImageAllocationDTO imageAllocationDTO : imageAllocationDTOList) {
            if (imageAllocationDTO.getCluster() != null) {
                allocated++;
                if (imageAllocationDTO.getCluster()) {
                    imageSet.add(new CourseImageAllocationVO.Image(imageAllocationDTO.getClusterName(),
                            ImageTypeEnum.CLUSTER.getMessage(), imageAllocationDTO.getBroken()));
                } else {
                    imageSet.add(new CourseImageAllocationVO.Image(imageAllocationDTO.getImageName(),
                            ImageTypeEnum.SINGLE.getMessage(), false));
                }
            }
        }

        courseImageAllocationVO.setAllocatedCount(allocated + "/" + total);
        courseImageAllocationVO.setAllocated(allocated > 0);
        courseImageAllocationVO.setImages(Lists.newArrayList(imageSet));
    }


    private void checkFlavorValid(Boolean cluster, String flavor) {
        String[] flavorArray = StringUtils.split(flavor, ",");
        if ((cluster && flavorArray.length != 6) || (!cluster && flavorArray.length != 2)) {
            throw new GlobalException(ResultEnum.BIND_ERROR.fillArgs("配置不符合规则"));
        }
    }

    private void checkClusterExists(Long clusterId) {
        if (clusterId == null) {
            throw new GlobalException(ResultEnum.BIND_ERROR.fillArgs("集群Id不能为空"));
        }
        Cluster cluster = clusterMapper.selectByPrimaryKey(clusterId);
        if (cluster == null) {
            throw new GlobalException(ResultEnum.CLUSTER_NOT_EXISTS);
        }
        if (cluster.getBroken()) {
            throw new GlobalException(ResultEnum.CLUSTER_BROKEN);
        }
    }

    private void checkImageExists(Long imageId) {
        if (imageId == null) {
            throw new GlobalException(ResultEnum.BIND_ERROR.fillArgs("镜像Id不能为空"));
        }
        Image image = imageMapper.selectByPrimaryKey(imageId);
        if (image == null) {
            throw new GlobalException(ResultEnum.IMAGE_NOT_EXISTS);
        }
    }
}
